package wangwenjun.phase3.executor;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;

import static java.util.stream.Collectors.toList;

/**
 * @author ChangLiang
 * @date 2020/6/23
 */
@Slf4j
public class SubmitTest {

    @Test
    @DisplayName("test executorService#submit")
    public void test() throws InterruptedException {

        /**
         * 这个方法执行结果告诉我们 如果打断了 那么queue里面的3个任务 2,3,4没有执行
         * 另外 任务1被打断了 也没有执行完成
         * 如何能够补偿这没有完成的4个任务呢
         */

        final ExecutorService executorService = Executors.newSingleThreadExecutor();
        List<Runnable> tasks = IntStream.range(0, 5).boxed().map(SubmitTest::toTask).collect(toList());
        tasks.forEach(r->executorService.submit(r));
        TimeUnit.SECONDS.sleep(12);
        List<Runnable> runnables = executorService.shutdownNow();
        // [java.util.concurrent.FutureTask@15b3e5b, java.util.concurrent.FutureTask@61ca2dfa, java.util.concurrent.FutureTask@4b53f538]
        System.out.println(runnables);
    }

    private static Runnable toTask(int i) {
        return () -> {
            try {
                log.info("The TaskId:{} will be executed", i);
                TimeUnit.SECONDS.sleep(i * 5 + 10);
                log.info("The TaskId:{} execute finished", i);
            } catch (InterruptedException e) {
                log.warn("The TaskId:{} is interrupted", i);
            }
        };
    }

    @Test
    @DisplayName("test callable wrapper")
    public void test2() throws InterruptedException {
        final ExecutorService executorService = Executors.newSingleThreadExecutor();
        List<Callable<Integer>> tasks = IntStream.range(0, 5).boxed().map(MyTask::new).collect(toList());
        tasks.forEach(r->executorService.submit(r));
        TimeUnit.SECONDS.sleep(12);
        List<Runnable> runnables = executorService.shutdownNow();
        System.out.println(runnables);
        tasks.stream().filter(callable-> !((MyTask) callable).isSuccess())
                .forEach(myTask->log.info("TaskId:{} not finished",((MyTask)myTask).taskId));
    }

    private static class MyTask implements Callable<Integer> {

        private final Integer taskId;

        private boolean success = false;

        MyTask(Integer taskId) {
            this.taskId = taskId;
        }


        @Override
        public Integer call() throws Exception {

            log.info("The TaskId:{} will be executed.\n", taskId);
            TimeUnit.SECONDS.sleep(taskId * 5 + 10);
            System.out.printf("The TaskId:{} execute finished.\n", taskId);
            success = true;
            return taskId;
        }

        public boolean isSuccess() {
            return success;
        }
    }

    @Test
    @DisplayName("test runnable wrapper")
    public void test3() throws InterruptedException {
        final ExecutorService executorService = Executors.newSingleThreadExecutor();
        List<Runnable> tasks = IntStream.range(0, 5).boxed().map(MyRunnable::new).collect(toList());
        tasks.forEach(r->executorService.submit(r));
        TimeUnit.SECONDS.sleep(12);
        List<Runnable> runnables = executorService.shutdownNow();
        System.out.println(runnables);
        tasks.stream().filter(r-> !((MyRunnable) r).isSuccess())
                .forEach(r->log.info("TaskId:{} not finished",((MyRunnable)r).taskId));
    }

    private static class MyRunnable implements Runnable {

        private final Integer taskId;

        private boolean success = false;

        MyRunnable(Integer taskId) {
            this.taskId = taskId;
        }

        @Override
        public void run() {
            log.info("The TaskId:{} will be executed.\n", taskId);
            try {
                TimeUnit.SECONDS.sleep(taskId * 5 + 10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.printf("The TaskId:{} execute finished.\n", taskId);
            success = true;
        }

        public boolean isSuccess() {
            return success;
        }
    }
}
